(算法)金钗赛诗

题目:

赛诗会后,十二金钗待奔前程。分别宴上,12人各写了一首诗放入包囊。

大家随机取一个,若取到自己的诗,则再取一个,并放回自己的诗,12人都拿到诗算一种分配。

请问:共有多少种不同的分配?

思路:

问题简化:给定n个人写n首诗,要求赠给其他人,共有多少种分配方法。

通俗一点就是:1到n的全排列,第i个数不是i的排列共有多少种?

其实这就是“错位排列”。

(错位排列问题的答案可以由“错排公式”求得,关于错排公式的推导可以参考百度百科:http://baike.baidu.com/view/668994.htm)

这里简单从类似动态规划的角度来分析问题:

假设n个数的错位排列数目为dp[n]

第n个数可以放置在1...(n-1)之间的任何一个位置,共(n-1)种方法;

假设第n个数放在了第k个位置,那么对于数字k而言:

  • 要么放在第n个位置;
  • 要么不放在第n个位置;

如果放在了第n个位置,那么相当于n和k做了交换,剩下的就是其他(n-2)个数的错位排列了,即dp[n-2];

如果不放在第n个位置,那么剩余(n-1)个数(可以将此时k看出原来的n,即此时剩下除了k位置外的所有数),即dp[n-1];

因此动态规划的状态转移方程为:

dp[n]=(n-1)*(dp[n-1]+dp[n-2]);

初始状态:

当n=1;dp[1]=0;

当n=2;dp[2]=1;(两个数,只存在一种错排的可能)

通过上述公式的递推可以得到“错排公式”:

D(n) = n! [1/0! - 1/1! + 1/2! - 1/3! + 1/4! + ... + (-1)^n/n!].

代码:

#include <iostream>

using namespace std;

int miscombination_1(int n){
    int A[n+1];
    A[1]=0;
    A[2]=1;
    for(int i=3;i<=n;i++)
        A[i]=(i-1)*(A[i-1]+A[i-2]);
    return A[n];
}

int miscombination_2(int n){
    if(n<=2)
        return n-1;
    int first=0;
    int second=1;
    int third=0;
    for(int i=3;i<=n;i++){
        third=(i-1)*(first+second);
        first=second;
        second=third;
    }
    return third;
}

int main()
{
    cout << miscombination_1(12) << endl;
    cout << miscombination_2(12) << endl;
    return 0;
}

问题答案:

176214841

posted @ 2015-07-10 16:25  AndyJee  阅读(624)  评论(2编辑  收藏  举报